﻿#include<iostream>
#include <xlnt/xlnt.hpp>
#include<afxwin.h>
#include <wchar.h>
#include <thread>
using namespace std;

#define KEY_PRESSED(VK_NONAME) ((GetAsyncKeyState(VK_NONAME) & 0x0001) ? 1:0) //如果为真，表示按下过
#define KEY_PRESSING(VK_NONAME) ((GetAsyncKeyState(VK_NONAME) & 0x8000) ? 1:0)  //如果为真，表示正处于按下状态

struct g_oper {
    string keyName;
    int key; //键码值
    int down; //按下延迟
    int up; //弹起延迟
    friend std::ostream& operator <<(std::ostream& os, g_oper const& a) {
        return os << '\n'
            << "热键\t= " << a.keyName << "" << '\n'
            << "按下\t= " << a.down << "" << '\n'
            << "弹起\t= " << a.up << "" << '\n'
            ;
    }
};

struct g_script {
    int idx;
    string name;
    bool enable;
    int key;
    string keyName;
    int loop;
    friend std::ostream& operator <<(std::ostream& os, g_script const& a) {
        return os << '\n'
            << "名称\t= " << a.name << "" << '\n'
            //<< "启用\t= " << (a.enable ? "是" : "否") << "" << '\n'
            << "热键\t= " << a.keyName << "" << '\n'
            << "循环\t= " << a.loop << "" << '\n'
            ;
    }
};

// 是否已暂停
bool pause = false;
// 窗口句柄
HWND hwnd;
// 脚本列表
std::vector<g_script> scriptList = {};
std::vector<vector<g_oper>> operAllList = {};
std::map<int, bool> finishMap;
// 前台模拟
bool frontSimulation = FALSE;

// 模拟按键
void keyDown(BYTE bVk) {
    while (pause) {
        Sleep(500);
    }
    //keybd_event(bVk, 0, 0, 0);
    PostMessage(hwnd, WM_KEYDOWN, bVk, 0);
}
void keyUp(BYTE bVk) {
    //keybd_event(bVk, 0, KEYEVENTF_KEYUP, 0);
    PostMessage(hwnd, WM_KEYUP, bVk, 0);
}

std::string string_To_UTF8(const std::string& str)
{
    int nwLen = ::MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, NULL, 0);
    wchar_t* pwBuf = new wchar_t[nwLen + 1];//一定要加1，不然会出现尾巴
    ZeroMemory(pwBuf, nwLen * 2 + 2);
    ::MultiByteToWideChar(CP_ACP, 0, str.c_str(), str.length(), pwBuf, nwLen);
    int nLen = ::WideCharToMultiByte(CP_UTF8, 0, pwBuf, -1, NULL, NULL, NULL, NULL);
    char* pBuf = new char[nLen + 1];
    ZeroMemory(pBuf, nLen + 1);
    ::WideCharToMultiByte(CP_UTF8, 0, pwBuf, nwLen, pBuf, nLen, NULL, NULL);
    std::string retStr(pBuf);
    delete[]pwBuf;
    delete[]pBuf;
    pwBuf = NULL;
    pBuf = NULL;
    return retStr;
}

//UTF-8编码格式字符串 转普通sting类型
std::string UTF8_To_string(const std::string& str)
{
    int nwLen = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, NULL, 0);
    wchar_t* pwBuf = new wchar_t[nwLen + 1];//一定要加1，不然会出现尾巴
    memset(pwBuf, 0, nwLen * 2 + 2);
    MultiByteToWideChar(CP_UTF8, 0, str.c_str(), str.length(), pwBuf, nwLen);
    int nLen = WideCharToMultiByte(CP_ACP, 0, pwBuf, -1, NULL, NULL, NULL, NULL);
    char* pBuf = new char[nLen + 1];
    memset(pBuf, 0, nLen + 1);
    WideCharToMultiByte(CP_ACP, 0, pwBuf, nwLen, pBuf, nLen, NULL, NULL);
    std::string retStr = pBuf;
    delete[]pBuf;
    delete[]pwBuf;
    pBuf = NULL;
    pwBuf = NULL;
    return retStr;
}

std::vector<std::vector<std::string>> readExcel(string sheetName)
{
    xlnt::workbook wb;
    wb.load(string_To_UTF8("脚本方案.xlsx"));
    auto ws = wb.sheet_by_title(string_To_UTF8(sheetName));
    std::vector<std::vector<std::string>> theWholeSpreadSheet;
    for (auto row : ws.rows(false)) {
        std::vector<std::string> aSingleRow;
        for (auto cell : row) {
            aSingleRow.push_back(cell.to_string());
        }
        theWholeSpreadSheet.push_back(aSingleRow);
    }
    return theWholeSpreadSheet;
}

bool isNumeric(std::string const& str) {
    auto it = str.begin();
    while (it != str.end() && std::isdigit(*it)) {
        it++;
    }
    return !str.empty() && it == str.end();
}

// string转int
int string_To_int(string s) {
    return atoi(s.c_str());
}

// 读取键值
int getKeyVal(string s) {
    if (s.size() > 2 && s.substr(0, 2) == "0x") {
        return std::stoi(s, nullptr, 16);
    }
    if (s.length() == 1) {
        if (isNumeric(s)) {
            if (s.length() == 1) {
                return 0x30 + atoi(s.c_str());
            }
        }
        else {
            return s[0];
        }
    }
    else if (s.compare("leftClick") == 0) return 0x01;
    else if (s.compare("rightClick") == 0) return 0x02;
    else if (s.compare("ctrl") == 0) return 17;
    else if (s.compare("space") == 0) return 32;
    else if (s.compare("tab") == 0) return 9;
    else {
        std::cout << "键码识别错误：" + s;
        exit(0);
        system("pause");
    }
}

// 加载配置
void loadConfig() {
    std::vector<std::vector<std::string>> mainSheetList = readExcel("主配置");
    int idx = 0;
    for (int rowInt = 1; rowInt < mainSheetList.size(); rowInt++) {
        g_script gs = {};
        gs.idx = idx++;
        gs.name = UTF8_To_string(mainSheetList.at(rowInt).at(0));
        gs.enable = UTF8_To_string(mainSheetList.at(rowInt).at(1)).compare("是") == 0;
        gs.keyName = mainSheetList.at(rowInt).at(2);
        gs.key = getKeyVal(gs.keyName);
        gs.loop = string_To_int(mainSheetList.at(rowInt).at(3));
        // 如果是启用的
        if (gs.enable) {
            scriptList.push_back(gs);
            std::cout << gs << "\t";
            // 加载操作配置
            std::vector<std::vector<std::string>> operSheetList = readExcel(gs.name);
            std::vector<g_oper> operList = {};
            for (int rowInt = 1; rowInt < operSheetList.size(); rowInt++) {
                g_oper go;
                go.keyName = operSheetList.at(rowInt).at(0);
                go.down = string_To_int(operSheetList.at(rowInt).at(1));
                go.up = string_To_int(operSheetList.at(rowInt).at(2));
                go.key = getKeyVal(go.keyName);
                operList.push_back(go);
            }
            operAllList.push_back(operList);
            finishMap.insert(std::make_pair(gs.idx, TRUE));
        }
    }
}

// 初始化
void init() {
    // 脚本信息
    SetConsoleTitle(L"魔法键鼠 v1.2.5");

    // 最小化窗口
    ShowWindow(GetConsoleWindow(), SW_MINIMIZE);

    // 提示信息
    std::cout << "    魔法键鼠 v1.2.5  --by gosick39（幻塔-学习交流Q群：565943273）" << endl << endl;
    std::cout << "  注意：本程序仅学习使用，禁止商用！" << endl << endl;
    std::cout << "  使用说明：1.在“脚本方案.xlsx”中编辑你的方案（热键/按键支持配置0x开头的16进制键码）" << endl;
    std::cout << "            2.暂停/继续键 F10" << endl;
    std::cout << "            3.停止键      F12" << endl;
  //  cout << endl;

    // 加载窗口句柄
    hwnd = FindWindowEx(nullptr, nullptr, L"UnrealWindow", nullptr);
    RECT rect;
    GetWindowRect(hwnd, &rect);
    int w = rect.right - rect.left, h = rect.bottom - rect.top;
    if (w <= 0 || h <= 0) {
        cout << endl << "警告：未捕获到幻塔手游窗口" << endl;
      //  system("pause");
       // exit(0);
    }

    // 加载配置
    loadConfig();
}

void handle(g_script gs, std::vector<g_oper> operList) {
    while (true) {
        for (int i = 0; i < operList.size(); i++) {
            g_oper go = operList.at(i);
            if (go.down >= 0) {
                keyDown(go.key);
                Sleep(go.down);
            }
            if (go.up >= 0) {
                keyUp(go.key);
                Sleep(go.up);
            }
        }
        gs.loop--;
        if (gs.loop <= 0) {
            finishMap[gs.idx] = TRUE;
            break;
        }
    }
}

// 热键处理
void hotKeyHandle() {
    while (TRUE) {
        if (KEY_PRESSED(VK_F10)) {
            pause = !pause;
        }
        if (KEY_PRESSED(VK_F12)) {
            // 获取当前控制台的窗口句柄
            HWND consoleWindow = GetConsoleWindow();
            // 启动一个新的控制台应用程序
            system("start MagicKM.exe");
            // 关闭当前控制台窗口
            PostMessage(consoleWindow, WM_CLOSE, 0, 0);
        }
        Sleep(200);
    }
}

int main() {
    // 初始化
    init();

    // 启动热键处理线程
    thread hotKeyHandleTh(hotKeyHandle);
    hotKeyHandleTh.detach();

    while (true) {
        // 遍历脚本
        for (int i = 0; i < scriptList.size(); i++) {
            g_script gs = scriptList.at(i);
            std::vector<g_oper> operList = operAllList.at(i);
            // 如果是启用的
            if (gs.enable) {
                if (finishMap[gs.idx] && KEY_PRESSED(gs.key)) {
                    // 如果幻塔窗口是活动窗口
                    if (GetForegroundWindow() == hwnd) {
                        finishMap[gs.idx] = FALSE;
                        // 启动操作线程
                        thread th(handle, gs, operList);
                        th.detach();
                    }
                }
            }
        }
        
        Sleep(100);
    }
    return 0;
}